#TODO: write documentation and look through code of load_csv
#TODO: write function for import from DIF
#TODO: write function for import from ods
#TODO: write a function load that can import all different kind of things

from symbide import ModuleManager
from numpy import matrix, array

#List of possible horizontal separators.
separators = (';',
              '\t',
              ':',
              ' ',
              '|',
              ',',
              )


numbers = (int,
           float,
           complex,
           )

def guess_split(buf):
    best_fit = None
    ratio = 0
    
    for s in separators:
        l = {}
        for row in buf.split('\n'):
            c = row.strip(s).count(s)
            if c==0:continue
            if c in l:
                l[c]+=1
            else:
                l[c]=1
        if len(l)>0:
            count = max(l.values())
        else:
            count = 0
            
        if count>ratio:
            best_fit = s
            ratio = count
        
    return best_fit

def mean(l):
    count=0
    for i in l:
        count+=len(i)
    return count/float(len(l))

def deviation(l, m=None):
    if m is None:
        m = mean(l)
    count=0
    for i in l:
        count+=len(i)**2
    return (count/float(len(l))-m**2)**(1.0/2)

def tonumber(arg):
    for c in numbers:
        try:
            b = c(arg)
            return b
        except:
            try:
                b = c(arg.replace(',','.'))
                return b
            except:
                pass
    raise ValueError

def parse_filter(string):
    if not string:
        return None
    
    for sep in (';', ',' ,'+' ,'&' ,'|', '\t', ' '):
        if string.count(sep)!=0:
            buf = string.split(sep)
            break
        else:
            buf = [string]
            
    for i in range(len(buf)):
        buf[i] = buf[i].strip(' \t\n')
        if len(buf[i])==0:
            buf[i] = None
            continue
        for sep in ('-', ':'):
            if len(buf[i])==1:
                if buf[i]==sep:
                    buf[i]=[0, None]
                    break 
            elif buf[i][0]==sep:
                buf[i] = (0, tonumber(buf[i].split(sep)[-1])+1)
                break
            elif buf[i][-1]==sep:
                buf[i] = [tonumber(buf[i].split(sep)[0]), None]
                break
            elif len(buf[i].split(sep))>1:
                buf[i] = (tonumber(buf[i].split(sep)[0]), tonumber(buf[i].split(sep)[-1])+1)
                break
        if isinstance(buf[i], str):
            try:
                num = tonumber(buf[i])
                buf[i] = (num, num+1)
            except:
                buf[i] = None
        
    for i in range(buf.count(None)):
        buf.remove(None)
        
    class Filter:
        def __init__(self, defList):
            self.defList = defList
            self.counter = 0
            self.length = 0
        
        def __iter__(self):
            return self
        
        def initialize(self, length):
            self.buf=[]
            for i in self.defList:
                if i[1]==None:
                    i[1] = length
                self.buf+=range(i[0], i[1])
            self.length = len(self.buf)
        
        def next(self):
            self.counter+=1
            if self.counter<=self.length:
                return self.buf[self.counter-1]
            else:
                self.counter = 0
                raise StopIteration
        
    return Filter(buf)
                

def filter_list(l, filter):
    if not filter:
        return l
    
    buf = []
    length = len(l)
    if hasattr(filter, 'initialize'):
        filter.initialize(length)
    
    if isinstance(filter, (tuple, list)) or hasattr(filter, 'next'):
        for i in filter:
            if i<length:
                buf.append(l[i])
                
    if type(filter)==type(lambda x:1):
        i = 1
        while True:
            try:
                pos = filter(i, length)
            except:
                try:
                    pos = filter(i)
                except:
                    break
            if pos>=length or pos<0:
                break
            buf.append(l[pos])
            i+=1
    return buf

def load_csv(path, hsplit=None, vsplit=None, row_filter=None, col_filter=None):
    f = open(path, 'r')
    buf = f.read()
    f.close()
    
    if not vsplit:
        vsplit = '\n'
    if not hsplit:
        hsplit = guess_split(buf)
        
    row_filter = parse_filter(row_filter)
    col_filter = parse_filter(col_filter)
        
    data=[]
    lines = buf.split(vsplit)
    lines = filter_list(lines, row_filter)
    for line in lines:
        row = []
        try:
            cols = line.split(hsplit)
        except:
            cols = [line]
        try:
            cols = filter_list(cols, col_filter)
        except:
            pass
        for num in cols:
            try:
                row.append(tonumber(num))
            except:
                row.append(num)
        data.append(row)
    
    try:
        return matrix(data)
    except:
        m = mean(data)
        d = deviation(data)
        top = m+d
        bottom = m-d
        buf = []
        for i in data:
            if len(i)<top and len(i)>bottom:
                buf.append(i)
        data = buf
                
    try:
        return matrix(data)
    except:
        maximum = len(max(data)) 
        for i in range(len(data)):
            data[i].extend([None]*(maximum-len(data[i])))
        
    return matrix(data)
